package com.aldobucchi.x.String
{
	import com.aldobucchi.x.RegExp.RegExpUtil;
	
	
	/**
	 * 
	 * Static utilities for working with strings
	 * Note that mx.utils and com.adobe.utils versions are not duplicated here
	 * ( that means, look there first! )
	 * 
	 * @author aldo.bucchi@gmail.com
	 * 
	 */	
	public class StringUtil
	{


		
		public static function repeat( str:String, count:int ):String
		{

			if ( count < 0 )
				return "";
				
			var str2:String = "";

			for ( var i:int=0; i < count ;i++ )
				str2 += str;

			return str2;
		}




		public static function decodeUnicode( str:String ):String
		{
		
			var r1:RegExp = / \\u  \w{4} /xg;
			var r2:RegExp = / \\U  \w{8} /xg;
			
			str = RegExpUtil.replaceWithCallback( r1, str, decodeUnicode_callback );
			str = RegExpUtil.replaceWithCallback( r2, str, decodeUnicode_callback );
		
		
			return str;
		
		}

			private static function decodeUnicode_callback( str:String ):String
			{
				str = str.substr( 2 ); // remove \U
				var c:Number = Number( "0x" + str );
				
				if ( isNaN( c ) )
					return "";
				
				return String.fromCharCode( c );			
			}







		/**
		 * 
		 * 
		 * 
		 * @param str
		 * @param quote
		 * @param escapeChar
		 * @return 
		 * 
		 */
		public static function findFirstQuotePair( str:String, quote:String = '"', escapeChar:String = "\\" ):Array
		{
			var indexes	:Array = [];
			var l		:int = str.length;
			for ( var i:int=0; i<l; i++ )
			{
				switch ( str.charAt( i ) )
				{
					case escapeChar:
						i++;	
					break;
					
					case quote:
						indexes.push( i );
						if ( indexes.length == 2 )
							return indexes;	
					break;
				}
			}
			return null;		
		}








		/**
		 * 
		 * limits are inclusive
		 * 
		 * 
		 * @param target
		 * @param beginIndex
		 * @param endIndex
		 * @return 
		 * 
		 */
		public static function removeSection( target:String, beginIndex:int, endIndex:int ):String
		{
			
			
			
			if ( beginIndex > endIndex ) return target;
			
			
			return target.substring( 0, beginIndex ) + target.substring( endIndex + 1 );
		}


		/**
		 * 
		 * 
		 * 
		 * 
		 * @param target
		 * @param insertion
		 * @param index
		 * @return 
		 * 
		 */
		public static function insertStringAtIndex( target:String, insertion:String, index:int ):String
		{
			return target.substring( 0 , index ) + insertion + target.substring( index );
		}
		
		
		/**
		 * 
		 * 
		 * 
		 * 
		 * @param target
		 * @param search
		 * @param before
		 * @param after
		 * @param caseSensitive
		 * @return 
		 * 
		 */		
		public static function wrapOccurrences( target:String, search:String, before:String, after:String, caseSensitive:Boolean = false ):String
		{
			
			if ( search.length == 0 ) // this should not happen
				return target.toString();
				
			var targetForSearch:String = caseSensitive ? target : target.toLowerCase();
			if ( ! caseSensitive ) search = search.toLowerCase();
			
			var iof:int;
			var startIndex:int = 0;
			var shift:int = 0;
			while ( true )
			{
				iof = targetForSearch.indexOf( search, startIndex );
				
				if ( iof == -1 )
					break;

				target = StringUtil.insertStringAtIndex( target, before, iof + shift );
				shift += before.length;
				
				target = StringUtil.insertStringAtIndex( target, after , iof + search.length + shift );
				shift += after.length;
				
				startIndex = iof + search.length + shift;
			}
			
			return target;		
		}
		
		
		/**
		 * 
		 * 
		 * @param string
		 * @param amount
		 * @return 
		 * 
		 */		
		public static function trimEdges( str:String, amount:int ):String
		{
			if ( ( amount * 2 ) > str.length )
				return "";
			return str.substring( amount, str.length - amount );
		}
		
		
		
		
		
		
		
		
		/**
		 * 
		 * Returns a random token that does not occurr in the string.
		 * The token is constructed by prepending prefix and an integer, starting from zero
		 * and incrementing on to infinity
		 * 
		 * @param str
		 * @param prefix
		 * @return 
		 * 
		 */		
		public static function getNonOccurringToken( str:String, prefix:String = "!@" ):String
		{
			
			var num		:int = 0;
			var token	:String = prefix + num;
			
			while ( str.indexOf( token ) != -1 )
			{
				token = prefix + ( ++num );		
			}
			
			return token;
				
		}
		
		
		
		
		
		/**
		 * 
		 * 
		 * 
		 * 
		 * @param target
		 * @param search
		 * @param before
		 * @param after
		 * @param caseSensitive
		 * @return 
		 * 
		 */		
		public static function wrapMultipleOccurrences( target:String, search:Array, before:String, after:String, caseSensitive:Boolean = false ):String
		{
			
			var token:String = getNonOccurringToken( target );
			
			
			for each ( var s:String in search )
				target = wrapOccurrences( target, s, token, token , caseSensitive );			
			var parts:Array = target.split( token );
			
			var result:String = "";
			while ( true )
			{
				result += parts.shift().toString();
				if ( parts.length == 0 )
					break;
				result += before + parts.shift().toString() + after;
			
			}
			return result;
		}
		
		
		
		
		public static function condenseWhiteSpace( str:String, replaceWith:String = " " ):String
		{
			return str.replace( /\s+/gi, replaceWith );			
		}
		
		
		
		/**
		 * 
		 * @param str
		 * @return an array of strings of length 1
		 * 
		 */		
		public static function toCharArray( str:String ):Array
		{
			
			for ( var i:int=0,l:int=str.length,arr:Array=[]; i<l; i++ ) 
				arr.push( str.charAt( i ) );
			return arr;		
		}
		

		public static function replaceMany( str:String, pairs:Array ):String
		{
			for each ( var pair:Array in pairs )
				str = replace( str, pair[0] as String, pair[1] as String );
			return str;
		}
		
		 
		/**
		 *For better performance, tests in the following order:
		* 1. null values
		* 2. lengths
		* 3. char by char
		 * 
		 * @param str1
		 * @param str2
		 * @return 
		 * 
		 */		 
		public static function stringsAreEqual( str1:String, str2:String ):Boolean
		{
			if ( str1 == null )
			{
				if ( str2 == null )
					return true;
					
				return false;
			}
			
			if ( str2 == null )
				return false;
			
			if ( str1.length != str2.length )
				return false;
			
			return ( str1 == str2 );
		}
		
		
		// copied from corelib because we cannot link com.adobe.utils.StringUtil ( fb3 bug )		
		public static function replace(input:String, replace:String, replaceWith:String):String
		{
			//change to StringBuilder
			var sb:String = new String();
			var found:Boolean = false;

			var sLen:Number = input.length;
			var rLen:Number = replace.length;

			for (var i:Number = 0; i < sLen; i++)
			{
				if(input.charAt(i) == replace.charAt(0))
				{   
					found = true;
					for(var j:Number = 0; j < rLen; j++)
					{
						if(!(input.charAt(i + j) == replace.charAt(j)))
						{
							found = false;
							break;
						}
					}

					if(found)
					{
						sb += replaceWith;
						i = i + (rLen - 1);
						continue;
					}
				}
				sb += input.charAt(i);
			}
			//TODO : if the string is not found, should we return the original
			//string?
			return sb;
		}		
		
		
		
		
		
		
		
	}
}